﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;

namespace GFluentCode.Tools
{
    /// <summary>
    /// 枚举类型扩展
    /// </summary>
    public static class EnumExtension
    {
        /// <summary>
        /// 获取枚举类型的含义
        /// </summary>
        /// <param name="en"></param>
        /// <returns></returns>
        public static string GDesc(this Enum en)
        {
            var objName = en.ToString();
            var t = en.GetType();
            var fi = t.GetField(objName);

            try
            {
                var arrDesc = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (arrDesc == null || arrDesc.Length <= 0) return en.ToString();

                return arrDesc[0].Description;
            }
            catch
            {
                return "未知";
            }
        }

        #region 值转枚举

        /// <summary>
        /// 值转枚举
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="v"></param>
        /// <returns></returns>
        public static T GEnum<T>(this object v) where T : Enum
        {
            return (T)Enum.Parse(typeof(T), v.ToString());
        }
        
        /// <summary>
        /// 值转枚举
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static T GEnum<T>(this int value) where T : struct,Enum
        {
            return value.ToString().GEnum<T>();
        }

        /// <summary>
        /// 值转枚举
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static T GEnum<T>(this int value,T defaultValue)where T : struct, Enum
        {
            return value.ToString().GEnum(defaultValue);
        }

        /// <summary>
        /// 值转枚举
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static T GEnum<T>(this string value) where T : struct, Enum
        {
            return value.GEnum(default(T));
        }

        /// <summary>
        /// 值转枚举
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static T GEnum<T>(this string value, T defaultValue) where T : struct, Enum
        {
            T result;
            if (value.GTryEnum(out result)) return result;
            return defaultValue;
        }

        #endregion

        /// <summary>
        /// 尝试解析枚举类型
        /// </summary>
        /// <param name="value"></param>
        /// <param name="enumType"></param>
        /// <param name="enumValue"></param>
        /// <returns></returns>
        public static bool GTryEnum(this string value, Type enumType, out object enumValue)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                enumValue = null;
                return false;
            }

            try
            {
                enumValue = Enum.Parse(enumType, value, true);
                return true;
            }
            catch
            {

            }

            foreach (object cur in Enum.GetValues(enumType))
            {
                if (cur.ToString().Equals(value, StringComparison.OrdinalIgnoreCase))
                {
                    enumValue = cur;
                    return true;
                }

                if (Convert.ToInt32(cur).ToString() == value)
                {
                    enumValue = cur;
                    return true;
                }

                if (value.Equals(((Enum)cur).GCode(false), StringComparison.OrdinalIgnoreCase))
                {
                    enumValue = cur;
                    return true;
                }

                if (value.Equals(((Enum)cur).GDesc(), StringComparison.OrdinalIgnoreCase))
                {
                    enumValue = cur;
                    return true;
                }
            }

            enumValue = null;
            return false;
        }

        /// <summary>
        /// 尝试解析枚举类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="enumValue"></param>
        /// <returns></returns>
        public static bool GTryEnum<T>(this string value, out T enumValue) where T : struct, Enum
        {
            if (Enum.TryParse<T>(value, out enumValue)) return true;

            object obj;
            if (value.GTryEnum(typeof(T), out obj))
            {
                enumValue = (T)((object)obj);
                return true;
            }

            enumValue = default(T);
            return false;
        }

        /// <summary>
        /// 枚举值转换为编码值(必须做 Description 绑定才能通过该函数获取枚举值的编码)。
        /// </summary>
        /// <param name="enum"></param>
        /// <param name="throwOnNotfound"></param>
        /// <returns></returns>
        public static string GCode(this Enum @enum, bool throwOnNotfound = false)
        {
            FieldInfo field = @enum.GetType().GetField(@enum.ToString());
            DescriptionAttribute bindingAttribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
            if (bindingAttribute != null)
            {
                return bindingAttribute.Description;
            }
            if (throwOnNotfound)
            {
                throw new Exception(string.Concat(
                    new string[]{ "枚举 ",
                                  @enum.GetType().FullName,
                                  ".",
                                  field.Name,
                                  " 未设置绑定 Binding，不能获取编码值。"
                }));
            }
            return "";
        }

        /// <summary>
        /// 转为键值对字典
        /// </summary>
        /// <param name="enumType"></param>
        /// <param name="excludeValues"></param>
        /// <returns></returns>
        public static IList<GPair<int>> GToDictionary(this Type enumType, params object[] excludeValues)
        {
            if (!enumType.IsEnum) throw new Exception($"{enumType.FullName}不是枚举");

            var enumerator = Enum.GetValues(enumType).GetEnumerator();

            var result = new List<GPair<int>>();

            try
            {
                while (enumerator.MoveNext())
                {
                    Enum @enum = enumerator.Current as Enum;
                    if (@enum == null || excludeValues.GExists(@enum)) continue;

                    result.Add(new GPair<int>(@enum.GDesc(), @enum.GetHashCode()));
                }
            }
            finally
            {
                var disposable = enumerator as IDisposable;
                if (disposable != null) disposable.Dispose();
            }

            return result;
        }
    }
}
